Dataformのクライアントライブラリを使ってワークスペース内のファイルを全て取得してみた

Dataformのクライアントライブラリを使ってワークスペース内のファイルを全て取得してみた

DataformのPythonクライアントライブラリを使用して、ワークスペース内のファイルとディレクトリをローカルにダウンロードするスクリプトを作成してみました。
Clock Icon2024.09.29

概要

Dataformにはpythonのクライアントライブラリが用意されています。python以外にもGo、Java、Node.jsのクライアントライブラリがあります。
https://cloud.google.com/dataform/docs/reference/libraries?hl=ja

クライアントライブラリを用いることでDataform APIをREST APIを用いるより簡潔に呼び出すことができます。
https://cloud.google.com/dataform/reference/rest

以前のブログではクライアントライブラリの動かし方や簡単な動作確認を行ってみました。
今回はクライアントライブラリを用いてDataformのワークスペースのファイルをローカルにダウンロードする処理を実装してみます。
※この記事の対象者はDataformの内蔵Gitを使用している方です。サードパーティのGit(GitHubなど)を利用している場合はあまり関係がない内容と考えます。

やりたいこと

  • Dataformのワークスペース内のファイルを全てローカルにダウンロードする、ディレクトリ構造も同一で。

これが今回やりたいことです。
2024/9月時点ではサードパーティのGitを使用していない場合、ワークスペースのファイルを全て取得するにはAPIを用いるか1ファイルずつコピペする方法しかないので。

過去には内蔵Gitのリポジトリからファイルを取得するプログラムを実装したこともありますが、今回はワークスペースからファイルを取得することが目標です。
内蔵Gitリポジトリからのファイル取得に関しては以下のブログをご参照ください。
https://dev.classmethod.jp/articles/20240911-dataform-api-getfiles/

動かしてみる

準備

まずはDataformのクライアントライブラリをインストールします。

pip install google-cloud-dataform

今回はCloud Shell上で実行するので特に認証情報は設定しませんがローカルで試す場合などは適宜認証情報を設定してください。
https://cloud.google.com/docs/authentication/application-default-credentials?hl=ja

使用するAPI

今回は以下の2つのAPIを使用します。

  • query_directory_contents: 指定されたワークスペース内のディレクトリの内容を取得します。レスポンスでは以下のようにファイルなのかディレクトリなのかがわかるようになっています。
file: ".gitignore"
file: "dataform.json"
directory: "definitions"

https://cloud.google.com/python/docs/reference/dataform/latest/google.cloud.dataform_v1beta1.services.dataform.DataformClient#google_cloud_dataform_v1beta1_services_dataform_DataformClient_query_directory_contents

  • read_file: 指定されたファイルの内容を読み取ります。

https://cloud.google.com/python/docs/reference/dataform/latest/google.cloud.dataform_v1beta1.services.dataform.DataformClient#google_cloud_dataform_v1beta1_services_dataform_DataformClient_read_file

注意点としては、query_directory_contentsAPIはディレクトリ階層を再帰的にクエリしてくれるわけではなく、指定した階層のデータのみを取得するAPIとなります。
よってディレクトリがあるワークスペースの場合に全てのファイルを取得するにはquery_directory_contentsのレスポンスでディレクトリだった場合にそのディレクトリを指定して再度APIを実行する再帰的な呼び出しを実装する必要があります。

まずはプログラムをご覧ください

以下が作成したプログラム全文です。

python
import os
from google.cloud import dataform_v1beta1
from google.cloud.dataform_v1beta1.types import QueryDirectoryContentsRequest, ReadFileRequest
from google.api_core.exceptions import NotFound

def query_directory_recursive(client, workspace_id, path="", local_base_path="."):
    # リクエストパラメータを初期化
    request = QueryDirectoryContentsRequest(
        workspace=workspace_id,
        path=path,
    )
    try:
        # リクエストを実行して、ディレクトリの内容を取得
        page_result = client.query_directory_contents(request=request)
        for response in page_result:
            response_str = str(response)  # レスポンスを文字列として処理
            if response_str.startswith("file: "):
                # ファイルの場合
                file_path = response_str[len("file: "):].strip('" \n')
                print(f"Processing file: {file_path}")
                # ファイルの内容を読み取る
                read_file(client, workspace_id, file_path, local_base_path)
            elif response_str.startswith("directory: "):
                # ディレクトリの場合
                dir_path = response_str[len("directory: "):].strip('" \n')
                print(f"Processing directory: {dir_path}")
                # ローカルにディレクトリを作成
                os.makedirs(os.path.join(local_base_path, dir_path), exist_ok=True)
                # ディレクトリ内を再帰的に検索
                query_directory_recursive(client, workspace_id, dir_path, local_base_path)
    except NotFound as e:
        # ディレクトリが見つからない場合のエラーハンドリング
        print(f"Error: {e.message}")

def read_file(client, workspace_id, file_path, local_base_path):
    # ファイル読み取りリクエストを初期化
    request = ReadFileRequest(
        workspace=workspace_id,
        path=file_path,
    )
    # ファイルの内容を取得
    response = client.read_file(request=request)
    # ローカルのファイルパスを構築
    local_file_path = os.path.join(local_base_path, file_path)
    # 必要なディレクトリが存在することを確認
    local_dir = os.path.dirname(local_file_path)
    os.makedirs(local_dir, exist_ok=True)
    # ファイルの内容をローカルファイルに書き込む
    with open(local_file_path, 'wb') as f:
        f.write(response.file_contents)

if __name__ == "__main__":
    # 自身の環境を設定
    project_id = "プロジェクトID"
    location = "asia-northeast1"
    repository_id = "リポジトリ名"
    workspace_id = "ワークスペース名"
    # ワークスペースの文字列を構築
    workspace_str = f"projects/{project_id}/locations/{location}/repositories/{repository_id}/workspaces/{workspace_id}"
    local_base_path = "./local_copy"
    # Dataformクライアントを作成
    client = dataform_v1beta1.DataformClient()
    # ディレクトリの再帰的な検索を開始
    query_directory_recursive(client, workspace_str, local_base_path=local_base_path)

処理の流れを簡単に説明します。

  1. query_directory_recursive 関数:
    目的: 指定されたワークスペース内のディレクトリ構造を再帰的に検索し、ファイルとディレクトリを処理。
    処理:
    ディレクトリの内容を取得 (client.query_directory_contents を使用)。
    各エントリをチェックし、ファイルならread_file関数を呼び出し、ディレクトリなら再帰的に自身を呼び出す。

  2. read_file 関数:
    目的: 指定されたファイルの内容を取得し、ローカルに保存。
    処理:
    ファイルの内容を取得 (client.read_file を使用)。
    ローカルのファイルパスを構築し、必要なディレクトリを作成。
    ファイルの内容をローカルファイルに書き込む。

  3. if name == "main"配下:
    目的: 必要なパラメータを設定し、Dataformクライアントを作成し、ディレクトリの再帰的な検索を開始。
    処理:
    必要なパラメータを設定。APIで指定するワークスペース名はprojects/{project_id}/locations/{location}/repositories/{repository_id}/workspaces/{workspace_id}の形式で作成する必要がある点に注意です。
    Dataformクライアントを作成して、query_directory_recursive 関数を呼び出して検索を開始します。

上記スクリプトを実行すると、local_base_pathで指定したディレクトリ名配下にワークスペースのファイルがディレクトリも含めて全て取得されます。

実行すると以下のようなファイル名ごとにPrintされて、ローカルにファイルが取得されます(表示される内容はワークスペースにより異なります)。

$ python get_workspace_contents.py 
Processing file: .gitignore
Processing file: dataform.json
Processing directory: definitions
Processing file: definitions/test_1.sqlx
Processing file: definitions/test_2.sqlx
Processing file: definitions/test_3.sqlx
...(以下略)

REST APIの時との比較

REST APIでリポジトリからファイル取得した時は以下のような実装でAPIを呼び出していました(抜粋です)。

url = f"https://dataform.googleapis.com/v1beta1/projects/{project_id}/locations/{location}/repositories/{repository}:queryDirectoryContents"
headers = {
    "Authorization": f"Bearer {fetch_token()}"
}
params = {
    "path": directory
}

自身でAPIのエンドポイントやヘッダーなどのリクエスト内容を作成しているのがわかるかと思います。

クライアントライブラリを用いるとAPIのエンドポイント設定は不要になり、リクエストの作成も簡潔になります。また組み込みのエラーハンドリングが提供されます。

request = QueryDirectoryContentsRequest(
    workspace=workspace_id,
    path=path,
)
client.query_directory_contents(request=request)

まとめ

ワークスペースのファイルを取得するプログラムをクライアントライブラリで実装してみました。
REST APIで実装するよりも労力が少ないなという印象です。
GitHubなどを連携されているワークロードではあまり意味がないスクリプトですが、内蔵Gitを用いている場合であればワークスペースから本スクリプトでデータを抜いてDataform CLIでコンパイルするとか意外と色々な使い方があるかもしれません。

Dataformのクライアントライブラリにも少し慣れてきた気がします。
それではまた。ナマステー

参考

https://cloud.google.com/python/docs/reference/dataform/latest

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.